package com.filex.ads.banner;

import android.annotation.SuppressLint;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;

import com.filex.ads.RequestParams;
import com.filex.ads.common.AdError;
import com.filex.ads.common.AdFormat;
import com.filex.ads.common.AdUrlGenerator;
import com.filex.ads.common.ClientMetadata;
import com.filex.ads.common.logging.Logger;
import com.filex.ads.common.utils.DeviceUtils;
import com.filex.ads.common.utils.Dips;
import com.filex.ads.network.AdLoader;
import com.filex.ads.network.AdResponse;
import com.filex.ads.network.FilexNetworkError;
import com.filex.ads.volley.NetworkResponse;
import com.filex.ads.volley.Request;
import com.filex.ads.volley.VolleyError;

import org.json.JSONArray;
import org.json.JSONObject;

import static android.Manifest.permission.ACCESS_NETWORK_STATE;

public class BannerAdController {

    static final int DEFAULT_REFRESH_TIME_MILLISECONDS = 30000;  // 30s
    private static final FrameLayout.LayoutParams WRAP_AND_CENTER_LAYOUT_PARAMS =
            new FrameLayout.LayoutParams(
                    FrameLayout.LayoutParams.WRAP_CONTENT,
                    FrameLayout.LayoutParams.WRAP_CONTENT,
                    Gravity.CENTER);

    private Context mContext;
    private FilexAdView mFilexAdView;
    private AdSize mAdSize;
    private String mPlacementId;
    private Integer mRefreshTimeMillis;

    private AdUrlGenerator mUrlGenerator;
    private Request mActiveRequest;
    private AdLoader mAdLoader;
    private final AdLoader.Listener mAdListener;
    private final Runnable mRefreshRunnable;
    private FilexWebView mWebView;
    private boolean mIsDestroyed;
    private Handler mHandler;

    private boolean mCurrentAutoRefreshStatus = true;

    private boolean mShouldAllowAutoRefresh = true;
    private boolean mAdWasLoaded;


    public BannerAdController(@NonNull Context context, @NonNull FilexAdView view) {
        mContext = context;
        mFilexAdView = view;

        mUrlGenerator = new AdUrlGenerator(mContext.getApplicationContext());

        mAdListener = new AdLoader.Listener() {
            @Override
            public void onSuccess(final AdResponse response) {
                onAdLoadSuccess(response);
            }

            @Override
            public void onErrorResponse(final VolleyError volleyError) {
                onAdLoadError(volleyError);
            }
        };

        mRefreshRunnable = new Runnable() {
            public void run() {
                loadAd();
            }
        };
        mRefreshTimeMillis = DEFAULT_REFRESH_TIME_MILLISECONDS;
        mHandler = new Handler(Looper.getMainLooper());
    }

    void onAdLoadSuccess(@NonNull final AdResponse adResponse) {
        mActiveRequest = null;
        try {
            JSONArray jsonArray = adResponse.getJsonBody();
            JSONObject adJson = jsonArray.getJSONObject(0);
            showBanner(adJson.getString("adm"));
            scheduleRefreshTimerIfEnabled();
        } catch (Exception e) {
            e.printStackTrace();
            onAdLoadError(new VolleyError(e));
        }
    }

    private void showBanner(String htmlResponse) {
        if (mWebView == null) {
            mWebView = new FilexWebView(mContext);
            mWebView.init(new HtmlWebViewListener() {
                @Override
                public void onLoaded() {
                    adPresentedOverlay();
                }

                @Override
                public void onFailed(AdError adError) {

                }

                @Override
                public void onClicked() {
                    adDidClick();
                }
            });
        }
        mWebView.loadHtmlResponse(htmlResponse);
        setAdContentView(mWebView);
        mFilexAdView.adLoaded();
    }

    void onAdLoadError(final VolleyError error) {
        final AdError errorCode = getAdErrorFromVolleyError(error, mContext);
        adDidFail(errorCode);
    }

    void adDidClick() {
        FilexAdView bannerAdView = getFilexAdView();
        if (bannerAdView == null) {
            return;
        }
        mFilexAdView.adClicked();
    }

    void adPresentedOverlay() {
        FilexAdView bannerAdView = getFilexAdView();
        if (bannerAdView == null) {
            return;
        }
        mFilexAdView.adPresentedOverlay();
    }

    @NonNull
    static AdError getAdErrorFromVolleyError(@NonNull final VolleyError error,
                                             @Nullable final Context context) {
        final NetworkResponse networkResponse = error.networkResponse;
        if (error instanceof FilexNetworkError) {
            switch (((FilexNetworkError) error).getReason()) {
                case BAD_BODY:
                    return AdError.BAD_REQUEST;
                case NO_FILL:
                    return AdError.NO_FILL;
                default:
                    return AdError.UNSPECIFIED;
            }
        }

        if (networkResponse == null) {
            if (!DeviceUtils.isNetworkAvailable(context)) {
                return AdError.CONNECTION_ERROR;
            }
            return AdError.UNSPECIFIED;
        }

        if (error.networkResponse.statusCode >= 400) {
            return AdError.INTERNAL_ERROR;
        }

        return AdError.UNSPECIFIED;
    }

    @Nullable
    public FilexAdView getFilexAdView() {
        return mFilexAdView;
    }

    public void loadAd() {
        internalLoad();
    }

    private void internalLoad() {
        mAdWasLoaded = true;
        if (TextUtils.isEmpty(mPlacementId)) {
            Logger.e("must set placementId before loadAd");
            return;
        }
        if (mAdSize == null) {
            Logger.e("must set adSize before loadAd");
            return;
        }
        if (!isNetworkAvailable()) {
            scheduleRefreshTimerIfEnabled();
            return;
        }
        if (mActiveRequest != null) {
            Logger.d("ad is loading for placement id : " + mPlacementId);
            return;
        }

        String adUrl = generateAdUrl();
        if (adUrl == null) {
            adDidFail(AdError.NO_FILL);
            return;
        }
        FilexAdView bannerAdView = getFilexAdView();
        if (bannerAdView == null || mContext == null) {
            Logger.d("Can't load an ad in this ad view because it was destroyed.");
            setNotLoading();
            return;
        }
        if (mAdLoader == null) {
            mAdLoader = new AdLoader(adUrl, AdFormat.BANNER, mPlacementId, mContext, mAdListener);
        }
        mActiveRequest = mAdLoader.loadAd();
    }


    void setNotLoading() {
        if (mActiveRequest != null) {
            if (!mActiveRequest.isCanceled()) {
                mActiveRequest.cancel();
            }
            mActiveRequest = null;
        }
        mAdLoader = null;
    }


    public String getPlacementId() {
        return mPlacementId;
    }

    public void setPlacementId(@NonNull String pid) {
        mPlacementId = pid;
    }

    public void setAdSize(AdSize adSize) {
        this.mAdSize = adSize;
    }

    public AdSize getAdSize() {
        return mAdSize;
    }

    public int getAdWidth() {
        return !mAdSize.isFillWidth() ? mAdSize.getWidth() : ClientMetadata.getInstance(mContext).getDeviceScreenWidthDip();
    }

    public int getAdHeight() {
        return mAdSize.getHeight();
    }

    public boolean getAutorefreshEnabled() {
        return getCurrentAutoRefreshStatus();
    }

    public boolean getCurrentAutoRefreshStatus() {
        return mCurrentAutoRefreshStatus;
    }

    void pauseRefresh() {
        setAutoRefreshStatus(false);
    }

    void resumeRefresh() {
        if (mShouldAllowAutoRefresh) {
            setAutoRefreshStatus(true);
        }
    }

    void setShouldAllowAutoRefresh(final boolean shouldAllowAutoRefresh) {
        mShouldAllowAutoRefresh = shouldAllowAutoRefresh;
        setAutoRefreshStatus(shouldAllowAutoRefresh);
    }

    private void setAutoRefreshStatus(final boolean newAutoRefreshStatus) {
        mCurrentAutoRefreshStatus = newAutoRefreshStatus;
        if (mAdWasLoaded && mCurrentAutoRefreshStatus) {
            scheduleRefreshTimerIfEnabled();
        } else if (!mCurrentAutoRefreshStatus) {
            cancelRefreshTimer();
        }
    }

    void forceRefresh() {
        setNotLoading();
        loadAd();
    }

    @Nullable
    String generateAdUrl() {
        if (mUrlGenerator == null) {
            return null;
        }

        final String requestUrl = mUrlGenerator.withAdFormat(AdFormat.BANNER)
                .withRequestParams(new RequestParams.Builder().width(getAdWidth()).height(getAdHeight()).build())
                .generateUrlString(mPlacementId);

        return requestUrl;
    }

    void adDidFail(AdError adError) {
        Logger.i("Ad failed to load.");
        setNotLoading();

        FilexAdView bannerAdView = getFilexAdView();
        if (bannerAdView == null) {
            return;
        }
        scheduleRefreshTimerIfEnabled();
        mFilexAdView.adFailed(adError);
    }

    void scheduleRefreshTimerIfEnabled() {
        cancelRefreshTimer();
        if (mCurrentAutoRefreshStatus && mRefreshTimeMillis != null && mRefreshTimeMillis > 0) {
            mHandler.postDelayed(mRefreshRunnable, mRefreshTimeMillis);
        }
    }

    private void cancelRefreshTimer() {
        mHandler.removeCallbacks(mRefreshRunnable);
    }

    @SuppressLint("MissingPermission")
    private boolean isNetworkAvailable() {
        if (mContext == null) {
            return false;
        }
        // If we don't have network state access, just assume the network is up.
        if (!DeviceUtils.isPermissionGranted(mContext, ACCESS_NETWORK_STATE)) {
            return true;
        }

        ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = null;
        if (cm != null) {
            networkInfo = cm.getActiveNetworkInfo();
        }
        return networkInfo != null && networkInfo.isConnected();
    }

    private void setAdContentView(final View view) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                FilexAdView bannerAdView = getFilexAdView();
                if (bannerAdView == null) {
                    return;
                }
                bannerAdView.removeAllViews();
                bannerAdView.addView(view, getAdLayoutParams());
            }
        });
    }

    private FrameLayout.LayoutParams getAdLayoutParams() {
        int width = getAdWidth();
        int height = getAdHeight();
        if (width > 0 && height > 0) {
            int scaledWidth = Dips.asIntPixels(width, mContext);
            int scaledHeight = Dips.asIntPixels(height, mContext);
            return new FrameLayout.LayoutParams(scaledWidth, scaledHeight, Gravity.CENTER);
        } else {
            return WRAP_AND_CENTER_LAYOUT_PARAMS;
        }
    }


    Integer getRefreshTimeMillis() {
        return mRefreshTimeMillis;
    }

    void setRefreshTimeMillis(@Nullable final Integer refreshTimeMillis) {
        mRefreshTimeMillis = refreshTimeMillis;
    }


    void destroy() {
        if (mIsDestroyed) {
            return;
        }

        setNotLoading();

        setAutoRefreshStatus(false);
        cancelRefreshTimer();

        mFilexAdView = null;
        mContext = null;
        mUrlGenerator = null;
        mIsDestroyed = true;
        try {
            if (mWebView != null) {
                mWebView.destroy();
            }
        } catch (Exception e) {

        }

    }

    boolean isDestroyed() {
        return mIsDestroyed;
    }

}
